/*
 * Copyright (c) 2024. hallele, Inc All rights reserved.
 */

package cn.hallele.ms.starter.web.filter;

import cn.hallele.infrastructure.metadata.constant.MsgCodeConst;
import cn.hallele.infrastructure.metadata.exception.HalException;
import cn.hallele.infrastructure.metadata.io.Metric;
import cn.hallele.infrastructure.metadata.io.Response;
import cn.hallele.infrastructure.util.DateUtil;
import cn.hallele.infrastructure.util.NetUtil;
import cn.hallele.infrastructure.util.ObjectUtil;
import cn.hallele.infrastructure.util.SpringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Nonnull;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpHeaders;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * BaseFilter
 *
 * @author anle5
 * @since 2024/4/20 21:27
 */
@Slf4j
public abstract class BaseFilter extends OncePerRequestFilter {
    protected final static ObjectMapper MAPPER = SpringUtil.getBean(ObjectMapper.class);
    protected final static MessageSource I18N = SpringUtil.getBean(MessageSource.class);

    /**
     * Same contract as for {@code doFilter}, but guaranteed to be
     * just invoked once per request within a single request thread.
     * See {@link #shouldNotFilterAsyncDispatch()} for details.
     * <p>Provides HttpServletRequest and HttpServletResponse arguments instead of the
     * default ServletRequest and ServletResponse ones.
     *
     * @param request     request
     * @param response    response
     * @param filterChain filterChain
     */
    @Override
    protected void doFilterInternal(
            @Nonnull HttpServletRequest request,
            @Nonnull HttpServletResponse response,
            @Nonnull FilterChain filterChain) throws IOException {
        try {
            doCusInternal(request, response, filterChain);
        } catch (Exception e) {
            log.error("Find a filter error", e);
            String errorCode = MsgCodeConst.FAILED;
            Object[] args;

            if (e instanceof HalException halException) {
                errorCode = halException.getMessage();
                args = halException.getArgs();
            } else {
                args = new Object[]{e.getMessage()};
            }

            Response<Void> failed = Response.<Void>builder()
                    .metric(Metric.builder()
                            .requestId(NetUtil.getRequestId())
                            .success(ObjectUtil.equals(errorCode, MsgCodeConst.SUCCESS))
                            .msgCode(errorCode)
                            .msgDesc(I18N.getMessage(errorCode, args, LocaleContextHolder.getLocale()))
                            .completedTime(DateUtil.nowDate())
                            .build())
                    .data(null)
                    .build();
            sendResponse(response, failed);
        }
    }

    /**
     * 自定义过滤器
     *
     * @param request     请求
     * @param response    响应
     * @param filterChain 过滤连
     * @throws Exception 异常
     */
    abstract protected void doCusInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws Exception;

    /**
     * 响应
     *
     * @param resp resp
     * @param msg  msg
     * @throws IOException e
     */
    protected void sendResponse(HttpServletResponse resp, Response<?> msg) throws IOException {
        resp.setHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
        resp.getWriter().print(MAPPER.writeValueAsString(msg));
    }
}
